import subprocess
import time
import matplotlib.pyplot as plt
import numpy as np
import os
import pandas as pd
from matplotlib.ticker import MaxNLocator

# 配置图表字体（使用默认英文字体）
def setup_font():
    import matplotlib
    matplotlib.use('Agg')  # 必须在 import plt 前调用
    """配置matplotlib字体设置"""
    plt.rcParams.update(plt.rcParamsDefault)
    plt.rcParams['axes.unicode_minus'] = False
    plt.rcParams['font.size'] = 10

# 编译多线程程序（如果不存在）
def compile_program():
    if not os.path.exists("thread_cpu_simulator"):
        print("Compiling multi-threaded program...")
        try:
            # 需要C++20支持以使用std::barrier
            result = subprocess.run(
                ["g++", "-std=c++20", "-pthread", "thread_cpu_simulator.cpp", "-o", "thread_cpu_simulator"],
                capture_output=True, text=True, encoding='utf-8'
            )
            if result.returncode != 0:
                print("Compilation failed!")
                print("Error output:", result.stderr)
                exit(1)
            print("Compilation successful")
        except Exception as e:
            print(f"Error during compilation: {e}")
            exit(1)
    else:
        print("Multi-threaded executable already exists, skipping compilation")

# 运行单次测试，返回单线程和多线程时间
def run_test(num_threads, compute_iterations, outer_iterations, run_single_thread=True, warmup=False):
    try:
        cmd = [
            "./thread_cpu_simulator",
            str(num_threads),
            str(compute_iterations),
            str(outer_iterations),
            "1" if run_single_thread else "0"
        ]
        
        if warmup:
            subprocess.run(cmd, capture_output=True, text=True, encoding='utf-8')
            return (0.0, 0.0, 0.0)
            
        result = subprocess.run(cmd, capture_output=True, text=True, encoding='utf-8')
        
        if result.returncode != 0:
            print(f"Test failed (threads: {num_threads}, compute iterations: {compute_iterations}):")
            print(result.stderr)
            return (None, None, None)
        
        single_time = None
        multi_time = None
        speedup = None
        
        for line in result.stdout.split('\n'):
            if "单线程耗时:" in line:
                parts = line.strip().split()
                if len(parts) >= 2:
                    single_time = float(parts[1])
            if "多线程耗时:" in line:
                parts = line.strip().split()
                if len(parts) >= 2:
                    multi_time = float(parts[1])
            if "加速比:" in line:
                parts = line.strip().split()
                if len(parts) >= 2:
                    speedup = float(parts[1])
        
        return (single_time, multi_time, speedup)
        
    except Exception as e:
        print(f"Error running test: {e}")
        return (None, None, None)

# 执行参数扫描测试
def run_parameter_sweep():
    test_configs = {
        "thread_count_test": {
            "fixed_compute": 1000,
            "fixed_outer": 100,
            "thread_counts": [1, 2, 4, 8, 12, 16, 24, 32]
        },
        "compute_intensity_test": {
            "fixed_threads": 8,
            "fixed_outer": 10,
            "compute_iterations": [100, 200, 400, 800, 1000, 2000, 4000, 8000, 10000, 50000, 100000, 150000, 200000]
        },
        "sync_frequency_test": {
            "fixed_threads": 8,
            "fixed_compute": 10,
            "outer_iterations": [2000, 4000, 8000, 10000, 40000, 80000, 100000]  # 外部循环次数即同步次数
        }
    }
    
    results = {
        "thread_count_test": [],
        "compute_intensity_test": [],
        "sync_frequency_test": []
    }
    
    print("Performing warm-up run...")
    run_test(2, 10000, 1, warmup=True)
    
    # 测试1: 固定计算量，改变线程数
    print("\nStarting thread count impact test...")
    cfg = test_configs["thread_count_test"]
    for thread_count in cfg["thread_counts"]:
        print(f"Testing with {thread_count} threads")
        single_time, multi_time, speedup = run_test(
            thread_count, cfg["fixed_compute"], cfg["fixed_outer"]
        )
        if single_time is not None and multi_time is not None:
            results["thread_count_test"].append({
                "threads": thread_count,
                "compute_iterations": cfg["fixed_compute"],
                "outer_iterations": cfg["fixed_outer"],
                "single_thread_time": single_time,
                "multi_thread_time": multi_time,
                "speedup": speedup
            })
    
    # 测试2: 固定线程数，改变计算量
    print("\nStarting computation intensity impact test...")
    cfg = test_configs["compute_intensity_test"]
    for compute_iter in cfg["compute_iterations"]:
        print(f"Testing with {compute_iter} compute iterations")
        single_time, multi_time, speedup = run_test(
            cfg["fixed_threads"], compute_iter, cfg["fixed_outer"]
        )
        if single_time is not None and multi_time is not None:
            results["compute_intensity_test"].append({
                "threads": cfg["fixed_threads"],
                "compute_iterations": compute_iter,
                "outer_iterations": cfg["fixed_outer"],
                "single_thread_time": single_time,
                "multi_thread_time": multi_time,
                "speedup": speedup
            })
    
    # 测试3: 固定线程数和计算量，改变同步频率（外部循环次数）
    print("\nStarting synchronization frequency impact test...")
    cfg = test_configs["sync_frequency_test"]
    for outer_iter in cfg["outer_iterations"]:
        print(f"Testing with {outer_iter} outer iterations (sync events)")
        single_time, multi_time, speedup = run_test(
            cfg["fixed_threads"], cfg["fixed_compute"], outer_iter
        )
        if single_time is not None and multi_time is not None:
            results["sync_frequency_test"].append({
                "threads": cfg["fixed_threads"],
                "compute_iterations": cfg["fixed_compute"],
                "outer_iterations": outer_iter,
                "single_thread_time": single_time,
                "multi_thread_time": multi_time,
                "speedup": speedup
            })
    
    return results

# 生成分析图表
def generate_plots(results):
    if not os.path.exists("results_thread"):
        os.makedirs("results_thread")
    
    # 1. 线程数对性能的影响
    plt.figure(figsize=(14, 8))
    data = results["thread_count_test"]
    if data:
        threads = [d["threads"] for d in data]
        single_times = [d["single_thread_time"] for d in data]
        multi_times = [d["multi_thread_time"] for d in data]
        speedups = [d["speedup"] for d in data]
        
        ax1 = plt.subplot(211)
        ax1.plot(threads, single_times, 'o-', color='b', label='Single Thread')
        ax1.plot(threads, multi_times, 'o-', color='r', label='Multi Thread')
        ax1.set_xlabel('Number of Threads')
        ax1.set_ylabel('Total Time (seconds)')
        ax1.set_title('Impact of Thread Count on Total Time (with Random Sync Delay)')
        ax1.grid(True)
        ax1.legend()
        ax1.xaxis.set_major_locator(MaxNLocator(integer=True))
        
        ax2 = plt.subplot(212)
        ax2.plot(threads, speedups, 'o-', color='g')
        ax2.axhline(y=1, color='r', linestyle='--', label='No Speedup')
        ax2.set_xlabel('Number of Threads')
        ax2.set_ylabel('Speedup (Single Thread Time / Multi Thread Time)')
        ax2.set_title('Impact of Thread Count on Speedup (with Random Sync Delay)')
        ax2.grid(True)
        ax2.legend()
        ax2.xaxis.set_major_locator(MaxNLocator(integer=True))
        
        plt.tight_layout()
        plt.savefig('results_thread/thread_count_analysis.png', dpi=300, bbox_inches='tight')
        plt.close()
    else:
        print("Warning: No valid data for thread count test, skipping plot")
    
    # 2. 计算强度对性能的影响
    plt.figure(figsize=(14, 8))
    data = results["compute_intensity_test"]
    if data:
        compute_iterations = [d["compute_iterations"] for d in data]
        single_times = [d["single_thread_time"] for d in data]
        multi_times = [d["multi_thread_time"] for d in data]
        speedups = [d["speedup"] for d in data]
        
        ax1 = plt.subplot(211)
        ax1.plot(compute_iterations, single_times, 'o-', color='b', label='Single Thread')
        ax1.plot(compute_iterations, multi_times, 'o-', color='r', label='Multi Thread')
        ax1.set_xlabel('Compute Iterations per Thread')
        ax1.set_ylabel('Total Time (seconds)')
        ax1.set_title(f'Impact of Computation Intensity on Time (with Random Sync Delay, {data[0]["threads"]} threads)')
        ax1.grid(True)
        ax1.legend()
        ax1.set_xscale('log')
        
        ax2 = plt.subplot(212)
        ax2.plot(compute_iterations, speedups, 'o-', color='g')
        ax2.axhline(y=1, color='r', linestyle='--', label='No Speedup')
        ax2.set_xlabel('Compute Iterations per Thread')
        ax2.set_ylabel('Speedup')
        ax2.set_title(f'Impact of Computation Intensity on Speedup (with Random Sync Delay, {data[0]["threads"]} threads)')
        ax2.grid(True)
        ax2.legend()
        ax2.set_xscale('log')
        
        plt.tight_layout()
        plt.savefig('results_thread/compute_intensity_analysis.png', dpi=300, bbox_inches='tight')
        plt.close()
    else:
        print("Warning: No valid data for computation intensity test, skipping plot")
    
    # 3. 同步频率对性能的影响（重点测试）
    plt.figure(figsize=(14, 8))
    data = results["sync_frequency_test"]
    if data:
        outer_iterations = [d["outer_iterations"] for d in data]
        single_times = [d["single_thread_time"] for d in data]
        multi_times = [d["multi_thread_time"] for d in data]
        speedups = [d["speedup"] for d in data]
        
        ax1 = plt.subplot(211)
        ax1.plot(outer_iterations, single_times, 'o-', color='b', label='Single Thread')
        ax1.plot(outer_iterations, multi_times, 'o-', color='r', label='Multi Thread')
        ax1.set_xlabel('Outer Iterations (Synchronization Events)')
        ax1.set_ylabel('Total Time (seconds)')
        ax1.set_title('Impact of Synchronization Frequency on Total Time (with Random Sync Delay)')
        ax1.grid(True)
        ax1.legend()
        ax1.xaxis.set_major_locator(MaxNLocator(integer=True))
        
        ax2 = plt.subplot(212)
        ax2.plot(outer_iterations, speedups, 'o-', color='g')
        ax2.axhline(y=1, color='r', linestyle='--', label='No Speedup')
        ax2.set_xlabel('Outer Iterations (Synchronization Events)')
        ax2.set_ylabel('Speedup')
        ax2.set_title('Impact of Synchronization Frequency on Speedup (with Random Sync Delay)')
        ax2.grid(True)
        ax2.legend()
        ax2.xaxis.set_major_locator(MaxNLocator(integer=True))
        
        plt.tight_layout()
        plt.savefig('results_thread/sync_frequency_analysis.png', dpi=300, bbox_inches='tight')
        plt.close()
    else:
        print("Warning: No valid data for synchronization frequency test, skipping plot")
    
    # 4. 汇总数据表格
    print("\nTest results summary:")
    for test_name, data in results.items():
        print(f"\n{test_name}:")
        if data:
            df = pd.DataFrame(data)
            df_en = df.rename(columns={
                'threads': 'Threads',
                'compute_iterations': 'Compute Iterations',
                'outer_iterations': 'Outer Iterations',
                'single_thread_time': 'Single Thread Time (s)',
                'multi_thread_time': 'Multi Thread Time (s)',
                'speedup': 'Speedup'
            })
            print(df_en)
            df_en.to_csv(f'results_thread/{test_name}_results.csv', index=False, encoding='utf-8')
        else:
            print("No valid test data")

# 主函数
def main():
    setup_font()
    compile_program()
    results = run_parameter_sweep()
    generate_plots(results)
    print("\nAll tests completed! Results saved to 'results_thread' directory")

if __name__ == "__main__":
    main()
    
